/* Andreas Baum
 * ConnectionManager.java
 * This class handles the information regarding the users
 * This class is also responsible for all interaction between users.
 * Anything that goes between two users goes through the Connection Manager
 * CS 78 - Networks - Programming Assignment 1
 */

package ChatServer;

import java.io.IOException;
import java.util.Hashtable;

public class ConnectionManager {

	private static Hashtable<String, ServerThread> connections;

	private static ConnectionManager cm;

	// Constructor is private so nobody can outside can call it
	// This guarantees uniqueness of the Connection Manager
	private ConnectionManager() {
		connections = new Hashtable<String, ServerThread>();
	}

	// Returns the connection manager if there is one. 
	// If not, creates one (the first one is created this way):
	public static ConnectionManager getConnectionManager() {

		if (cm==null)
			cm = new ConnectionManager();

		return cm;
	}

	// Updates the user list when a user joins the server
	// Informs all existing users.
	public void logInUser(String name, ServerThread thread){
		connections.put(name, thread);
		if(!connections.isEmpty()) {
			Object[] threads = connections.values().toArray();
			int i;
			for (i = 0; i< threads.length; i++) {
				try {
					((ServerThread) threads[i]).sendInfo(name + " has joined the server.");
					((ServerThread) threads[i]).updateUsers(getBuddyList());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	// Updates the user list when a user leaves the server
	// Informs all existing users.
	public void logOutUser(String name, Boolean quiet) {
		if (doesNameExist(name)) {
			connections.remove(name);
			if(!connections.isEmpty()) {
				Object[] threads = connections.values().toArray();
				int i;
				if (!quiet) {
					for (i = 0; i< threads.length; i++) {
						try {
							((ServerThread) threads[i]).sendInfo(name + " has left the server.");
							((ServerThread) threads[i]).updateUsers(getBuddyList());
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			}
		}
	}

	// Checks if a username is already being used:
	public boolean doesNameExist(String name) {
		return connections.containsKey(name);
	}

	// returns null if there is no mapping for the key:
	public ServerThread getThread(String name) {
		return ((ServerThread) connections.get(name));
	}

	// Sends a message to a user from another user:
	public void sendMessage(String destination, String sender, String string) {
		ServerThread receiver = getThread(destination);
		try {
			receiver.sendMessage(sender, string);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// Sends a global message to all users from one user:
	public void broadcastMessage(String sender, String string) {
		if(!connections.isEmpty()) {
			String preGlobalSend = ">>> ";
			int number_of_spaces = 25 - sender.length() - preGlobalSend.length();
			String spaces = "";
			int i;
			for (i = 0; i< number_of_spaces; i++) {
				spaces = spaces + " ";
			}
			String globalSend = preGlobalSend + sender + spaces;
			//System.out.println(globalSend + string);
			Object[] threads = connections.values().toArray();
			for (i = 0; i< threads.length; i++) {
				try {
					((ServerThread) threads[i]).sendGlobal(globalSend + string);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	// Sends a global emote to all users from one user:
	public void broadcastEmote(String sender, String string) {
		if(!connections.isEmpty()) {
			String preGlobalSend = "+++ ";
			int number_of_spaces = 25 - sender.length() - preGlobalSend.length();
			String spaces = "";
			int i;
			for (i = 0; i< number_of_spaces; i++) {
				spaces = spaces + " ";
			}
			String globalSend = preGlobalSend + sender + spaces;
			//System.out.println(globalSend + sender + " " + string);
			Object[] threads = connections.values().toArray();
			for (i = 0; i< threads.length; i++) {
				try {
					((ServerThread) threads[i]).sendGlobal(globalSend + sender + " " + string);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	// Returns the buddy list:
	public Object[] getBuddyList() {
		return connections.keySet().toArray();
	}

	// Disconnects everyone and closes:
	public void close() {
		int i;
		Object[] threads = connections.values().toArray();
		for (i = 0; i< threads.length; i++) {
			try {
				((ServerThread) threads[i]).sendGlobal("<<< Server shutting down >>>");
				((ServerThread) threads[i]).kill();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.exit(1);
	}

	// Used to forcibly boot another user, can only be used by admin:
	public void bootUser(String destination, String sender) {
		if ((sender.equals(AdminClient.ADMIN)) && (!destination.equals(AdminClient.ADMIN))) {
			ServerThread receiver = getThread(destination);
			receiver.boot(sender);
		}
	}

	// Used to silence a user, can only be used by admin:
	public void silenceUser(String destination, String sender) {
		if ((sender.equals(AdminClient.ADMIN)) && (!destination.equals(AdminClient.ADMIN))) {
			ServerThread receiver = getThread(destination);
			receiver.silence();
		}
	}

	// Used to unsilence a user, can only be used by admin:
	public void unsilenceUser(String destination, String sender) {
		if ((sender.equals(AdminClient.ADMIN)) && (!destination.equals(AdminClient.ADMIN))) {
			ServerThread receiver = getThread(destination);
			receiver.unsilence();
		}
	}
}